package com.admin.service.impl;

import com.admin.common.enums.*;
import com.admin.common.exception.BizException;
import com.admin.common.shiro.ShiroUtils;
import com.admin.common.thread.ThreadPoolManager;
import com.admin.common.util.BigDecimalUtil;
import com.admin.common.util.DateUtils;
import com.admin.domain.*;
import com.admin.mapper.*;
import com.admin.req.declarereturn.ReturnReq;
import com.admin.req.score.ScoreReq;
import com.admin.service.*;
import com.admin.vo.PageVO;
import com.admin.vo.ResultVO;
import com.admin.vo.declare.DeclareBatchScoreInfoVO;
import com.admin.vo.declare.DeclareListVO;
import com.admin.vo.declare.DeclareSampleVO;
import com.admin.vo.declare.DeclareTime;
import com.admin.vo.declareDetail.DeclareDetailVO;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author yangzhongliang
 * @date 2022/05/16
 */
@Slf4j
@Service
public class DeclareCityServiceImpl implements DeclareCityService {
    @Autowired
    private DeclareMapper declareMapper;
    @Autowired
    private AdminMapper adminMapper;
    @Autowired
    private DeclareService declareService;
    @Autowired
    private DeclareScoreService declareScoreService;
    @Autowired
    private DeclareReturnService declareReturnService;
    @Autowired
    private SysParamService sysParamService;
    @Autowired
    private AdminService adminService;
    @Autowired
    private SampleRecordService sampleRecordService;
    @Autowired
    private RegionsService regionsService;
    @Autowired
    private DeclareLogService logService;

    /**
     * 查询 申报的记录
     * @param pageIndex
     * @param pageSize
     * @param params
     * @return
     */
    @Override
    public ResultVO<PageVO<DeclareListVO>> getPageList(Integer pageIndex, Integer pageSize, Map<String, Object> params) {
        ResultVO<PageVO<DeclareListVO>> result = new ResultVO<>();
        PageVO<DeclareListVO> page = new PageVO<>(pageIndex, pageSize);
        List<DeclareListVO> declareListVOList = new ArrayList<>();
        // 查询总数
        // 市区 用户 - 查询自己区县内的所有申报 记录
        Integer cityIds = adminService.getResponsibleAreaIds(ShiroUtils.getUserId());
        params.put("cityIds", Arrays.asList(cityIds));
        //提交到市的数据
        params.put("isSubmitCity", 1);
        Integer totalCount = declareMapper.countByMap(params);
        page.setTotalCount(totalCount);
        if (totalCount > 0) {
            // 查询管理员信息
            params.put("startIndex", page.getStartIndex());
            params.put("pageSize", page.getPageSize());
            List<Declare> declareList = declareMapper.selectByMap(params);
            if (!CollectionUtils.isEmpty(declareList)) {
                for (Declare declare : declareList) {
                    DeclareListVO itemVO = new DeclareListVO();
                    BeanUtils.copyProperties(declare, itemVO);

                    itemVO.setDeclareStatusStr(DeclareStatusEnum.getText(declare.getDeclareStatus()));
                    itemVO.setDeclareTypeStr(DeclareTypeEnum.getText(declare.getDeclareType()));
                    itemVO.setCreateTimeStr(DateUtils.format(declare.getCreateTime(), DateUtils.DATE_PATTERN_TIME));
                    declareListVOList.add(itemVO);
                }
            }
        }
        page.setData(declareListVOList);

        //申报用户数量 和 实际上报数量
        Map<String,Object> resultMap = new HashMap<>();
        resultMap.put("totalUserQuantity", adminService.totalUserQuantity(MemberLevelEnum.CITY.getKey(), cityIds));
        resultMap.put("submitUserQuantity", adminService.currentRoundDeclareQuantity(MemberLevelEnum.CITY.getKey(), cityIds));
        page.setMap(resultMap);
        result.setData(page);
        result.setCode(EnumResult.SUCCESS.getCode());
        result.setMsg(EnumResult.SUCCESS.getMsg());
        return result;
    }


    /**
     * 市级 评分
     * @param userId
     * @param scoreReq
     * @return
     */
    @Override
    public ResultVO saveScore(Integer userId, ScoreReq scoreReq) {
        //查询当前登录用户所属社区
        AdminDO adminDO = adminMapper.selectByPrimaryKey(userId);
        if (Objects.isNull(adminDO)) {
            throw new BizException(-1, "登录信息已失效,请重新登录!");
        }

        return declareScoreService.save(userId, scoreReq, MemberLevelEnum.CITY.getKey());
    }

    @Override
    public ResultVO cityReturn(Integer userId, ReturnReq req) {
        return declareReturnService.save(userId, req, MemberLevelEnum.CITY.getKey());
    }

    @Transactional
    @Override
    public Boolean submitCity(Integer id) {
        Declare declare = declareMapper.selectByPrimaryKey(id);
        if (Objects.isNull(declare)) {
            throw new BizException(-1, "申报记录不存在或已删除!");
        }
        if (!declare.getScoreStatus().equals(DeclareScoreStatusEnum.CITY_SUCCESS.getKey())) {
            throw new BizException(-1, "市级未评分,暂不能提交上级!");
        }

        if (declare.getScoreStatus().equals(DeclareScoreStatusEnum.PROVINCE_WAIT.getKey())) {
            throw new BizException(-1, "已提交上级,勿重复操作!");
        }

        Declare declareUpdate = new Declare();
        declareUpdate.setId(declare.getId());
        declareUpdate.setScoreStatus(DeclareScoreStatusEnum.PROVINCE_WAIT.getKey());
        declareUpdate.setIsSubmitProvince(1);
        declareMapper.updateByPrimaryKeySelective(declareUpdate);

        //上级  市级 - 提交 省
        DeclareLog declareLog = new DeclareLog();
        declareLog.setDeclareId(declare.getId());
        declareLog.setUserLevel(ShiroUtils.getMemberLevel());
        declareLog.setUserLevelName(regionsService.getById(declare.getCityId()));
        declareLog.setContent("提交申报至【"+regionsService.getById(declare.getProvinceId())+"】");
        declareLog.setOperatorType(DeclareLogTypeEnum.TYPE_32.getKey());
        logService.insertLog(declareLog);
        return true;
    }

    @Override
    public Boolean batchSubmitCity(String ids) {
        if (StringUtils.isEmpty(ids)) {
            throw new BizException(-1, "你还没有选择记录!");
        }
        String[] strIds = ids.split(",");
        for (String strId : strIds) {
            if (Objects.isNull(strId)) {
                continue;
            }
            try {
                submitCity(Integer.parseInt(strId));
            } catch (Exception e) {
                log.error("batchSubmitCity error , id:{}, error:{}", strId, e);
            }
        }
        return true;
    }

    /**
     * 详情  -  如果退回  有退回原因
     * @param id
     * @return
     */
    @Override
    public DeclareDetailVO detail(Integer id) {
        Declare declare = declareMapper.selectByPrimaryKey(id);
        if (Objects.isNull(declare)) {
            throw new BizException(-1, "申报记录不存在或已删除!");
        }

        //通用详情方法 -
        //新增别的数据
        DeclareDetailVO declareDetailVO = declareService.detail(id, MemberLevelEnum.CITY.getKey());


        return declareDetailVO;
    }


    @Override
    public DeclareSampleVO toSample(Integer userId, Integer declareType) {
        //可能需要根据时间段区间查询,
        AdminDO adminDO = adminMapper.selectByPrimaryKey(userId);
        DeclareTime declareTime = sysParamService.getDeclareTime();
        DeclareSampleVO sampleVO = new DeclareSampleVO();
        // 如果已抽过 - 校验
        if (sampleRecordService.isSampled(userId, declareTime.getStartTime(), declareTime.getEndTime(), declareType)) {
            throw new BizException(-1, "市级已经抽过样了！");
        }

        Map<String,Object> params = new HashMap();
        params.put("startTime", DateUtils.dateFormatStr(declareTime.getStartTime()));
        params.put("endTime", DateUtils.dateFormatStr(declareTime.getEndTime()));
        params.put("declareType", declareType);
        //提交到市级
        params.put("isSubmitCity", 1);
        //待市级评分
        params.put("scoreStatus", DeclareScoreStatusEnum.CITY_WAIT.getKey());
        params.put("cityIds", Arrays.asList(new String[]{adminDO.getBusinessAreaIds()}));
        Integer quantity =  declareMapper.countByMap(params);
        if (null == quantity || quantity == 0) {
            throw new BizException(-1, "暂无需要抽样的申报数据!");
        }
        sampleVO.setQuantity(quantity);
        //涉及区县
        sampleVO.setAreaQuantity(declareMapper.countAreaNum(params));
        //涉及乡镇
        sampleVO.setCountryQuantity(declareMapper.countCountryNum(params));
        return sampleVO;
    }


    /**
     * 市级 抽样
     * @param userId
     * @param declareType
     * @param sampleRate
     * @param sampleScope
     * @return
     */
    @Override
    public DeclareSampleVO saveSample(Integer userId, Integer declareType, Double sampleRate, Integer sampleScope) {
        AdminDO adminDO = adminMapper.selectByPrimaryKey(userId);
        if (Objects.isNull(adminDO)) {
            throw new BizException(-1, EnumResult.USER_NOT_EXIST.getMsg());
        }
        DeclareTime declareTime = sysParamService.getDeclareTime();
        // 如果已抽过 - 校验
        if (sampleRecordService.isSampled(userId, declareTime.getStartTime(), declareTime.getEndTime(), declareType)) {
            throw new BizException(-1, "市级已经抽过样了！");
        }
        // 根据时间 -范围 - 抽样
        Map<String,Object> params = new HashMap();
        params.put("startTime", DateUtils.dateFormatStr(declareTime.getStartTime()));
        params.put("endTime", DateUtils.dateFormatStr(declareTime.getEndTime()));
        params.put("declareType", declareType);
        //提交到市级
        params.put("isSubmitCity", 1);
        //待市级评分
        params.put("scoreStatus", DeclareScoreStatusEnum.CITY_WAIT.getKey());
        params.put("cityIds", Arrays.asList(new String[]{adminDO.getBusinessAreaIds()}));

        List<Declare> declareList = declareMapper.selectByMap(params);
        //抽样后申报id结果
        List<Integer> sampledIds = new ArrayList<>();
        //根据区县分组
        Map<Integer, List<Declare>> areaGroupMap = declareList.stream().collect(Collectors.groupingBy(Declare::getAreaId));
        for (Map.Entry<Integer, List<Declare>> entry : areaGroupMap.entrySet()) {
            List<Integer> sampleDeclares = new ArrayList<>();
            List<Declare> areaDeclareList = entry.getValue();
            log.info("市级抽样，区县id：{}, 申报总数：{}", entry.getKey(), areaDeclareList.size());
            //区域待抽样 数量 = 区域数量 * 比例
            Double areaWaitSampleNumDouble = BigDecimalUtil.multiplyUp(areaDeclareList.size(), sampleRate/100);
            Integer areaWaitSampleNum = new Double(Math.ceil(areaWaitSampleNumDouble)).intValue();
            log.info("市级抽样，区县id：{}, 申报总数：{}, 抽样比例:{}, 抽样数量:{}", entry.getKey(), areaDeclareList.size(), sampleRate, areaWaitSampleNum);
            for (int i = 1; i <= areaWaitSampleNum; i++) {
               //随机抽样
               int randomIndex = new Random().nextInt(areaWaitSampleNum);
               //被抽样到的记录
               Declare sampledDeclare =  areaDeclareList.get(randomIndex);
               sampleDeclares.add(sampledDeclare.getId());
               sampledIds.add(sampledDeclare.getId());
            }
            log.info("市级抽样，区县id：{}, 申报总数：{}, 抽样比例:{}, 抽样结果:{}", entry.getKey(), areaDeclareList.size(), sampleRate, JSONObject.toJSON(sampleDeclares));
        }
        //封装抽样结果
        DeclareSampleRecord sampleRecord = new DeclareSampleRecord();
        sampleRecord.setDeclareStartTime(declareTime.getStartTime());
        sampleRecord.setDeclareEndTime(declareTime.getEndTime());
        sampleRecord.setSampleType(declareType);
        sampleRecord.setSampleRate(sampleRate.toString());
        sampleRecord.setSampleScope("0");
        sampleRecord.setSampleNum(sampledIds.size());
        JSONObject ob = new JSONObject();
        ob.put("sampledNum", sampledIds.size());
        ob.put("areaNum", areaGroupMap.keySet().size());
        ob.put("declareIds", sampledIds);
        sampleRecord.setSampleResult(ob.toString());
        sampleRecord.setRegionIds(adminDO.getResponsibleAreaIds());
        sampleRecord.setRegionNames(adminDO.getResponsibleArea());
        sampleRecord.setSampleLevel(MemberLevelEnum.CITY.getKey());
        sampleRecord.setCreateId(adminDO.getUserId());
        // 保存抽样记录 结果
        sampleRecordService.saveCitySample(userId, sampledIds, sampleRecord);

        DeclareSampleVO sampleVO = new DeclareSampleVO();
        sampleVO.setQuantity(sampledIds.size());
        sampleVO.setAreaQuantity(areaGroupMap.keySet().size());
        return sampleVO;
    }


    @Override
    public Integer batchScore(Integer userId, String declareIds) {
        AdminDO adminDO = adminMapper.selectByPrimaryKey(userId);
        Map<String,Object> params = buildBatchScorePrams(userId);
        List<Declare> declareList = declareMapper.selectByMap(params);
        if (CollectionUtils.isEmpty(declareList)) {
            throw new BizException(-1, "暂无需要赋分的申报数据!");
        }

        //异步处理
        ThreadPoolManager.get().execute(new Runnable() {
            @Override
            public void run() {
                for (Declare declare : declareList) {
                    declareScoreService.cityBatchScore(adminDO, declare);
                }
            }
        });
        return declareList.size();
    }

    @Override
    public DeclareBatchScoreInfoVO getBatchScoreInfo(Integer userId) {
        DeclareTime declareTime = sysParamService.getDeclareTime();
        if (!sampleRecordService.isSampled(userId, declareTime.getStartTime(), declareTime.getEndTime())) {
            throw new BizException(-1, "市级还没有抽样！");
        }
        DeclareBatchScoreInfoVO sampleVO = new DeclareBatchScoreInfoVO();
        //时间区间 内 - 非样本,未评分的申报 赋分
        Map<String,Object> params = buildBatchScorePrams(userId);
        Integer quantity =  declareMapper.countByMap(params);
        sampleVO.setQuantity(quantity);
        sampleVO.setItems(declareMapper.getGroupArea(params));
        return sampleVO;
    }

    private Map<String,Object> buildBatchScorePrams(Integer userId) {
        AdminDO adminDO = adminMapper.selectByPrimaryKey(userId);

        Map<String,Object> params = new HashMap<>();
        DeclareTime declareTime = sysParamService.getDeclareTime();
        params.put("startTime", DateUtils.dateFormatStr(declareTime.getStartTime()));
        params.put("endTime", DateUtils.dateFormatStr(declareTime.getEndTime()));
        params.put("isCitySample", 0);
        params.put("isSubmitCity", 1);
        params.put("cityIds", Arrays.asList(new String[]{adminDO.getBusinessAreaIds()}));
        params.put("scoreStatus", DeclareScoreStatusEnum.CITY_WAIT.getKey());
        return params;
    }
}
